import requests, datetime, os, time
from bs4 import BeautifulSoup
from lxml import etree
from enum import Enum
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException
from support.utils.toolkit import Log
from support.network.browserhelper import BrowserHelper
class Browser:
    TIMEOUT = 60
    WAIT_TIMEOUT_ONCE = 5
    ELEMENT_HIDDEN  = 0
    ELEMENT_VISIBLE = 1
    ELEMENT_ENABLED = 2
    TYPE = Enum("BROWSER",{
        'firefox': 'gecko',
        'chrome':'chrome',
        'edge':'edge'
    })

    def __init__(self, type, *, host=None, binpath=None, version=None):
        self.dm = BrowserHelper()
        self.driver = self.__createDriver__(type.name, host, binpath, version)

    def __createDriver__(self, typename, host, binpath, version):
        if(host):
            driver = BrowserHelper.remoteBrowser(typename, host=host)
        else:
            driver = BrowserHelper.localBrowser(typename, binpath, uri=self.dm.autoGetDriver(typename, version))
        return driver
            
    def isElementDisplay(self, element:tuple):
        self.findNodeBy((By.TAG_NAME, "body")).send_keys(Keys.END)
        return self.findNodeBy(element).is_displayed()

    def isElementEnable(self, element:tuple):
        return self.findNodeBy(element).is_enabled()

    def click(self, element:tuple):
        el = self.findNodeBy(element)
        if(el and el.is_displayed() and el.is_enabled()):
            try:
                el.click()
            except ElementClickInterceptedException:
                self.driver.execute_script("arguments[0].click();", el)
        time.sleep(1)
        return el != None
    
    def refresh(self):
        self.driver.refresh()
    
    def input(self, element:tuple, text:str):
        el = self.findNodeBy(element)
        if(el):el.send_keys(text)
        
    def dropDownClick(self, element:tuple, text:str):
        el = self.findNodeBy(element)
        if(el):
            option = el.find_element_by_visible_text(text)
            if(option):option.click()

    def __performKeys__(self, *keys):
        actions = ActionChains(self.driver)
        keyarray = [key for key in keys]
        for key in keyarray:
            actions.key_down(key)
        keyarray.reverse()
        for key in keyarray:
            actions.key_up(key)
        actions.perform() 
    
    def getChildBy(self, *, parentNode=None, method:tuple):
        if(not parentNode):
            parentNode = self.driver
        childrenCount = len(parentNode.find_elements(*method))
        for index in range(childrenCount):
            yield parentNode.find_elements(*method)[index]
    
    def findNodeBy(self, method:tuple, *, parentNode=None):
        if(not parentNode):
            parentNode = self.driver
        return parentNode.find_element(*method)

    def open(self, url, *, timeout:int = TIMEOUT, waitConditions:list, newTab:bool = False):
        if(self.driver is None):
            raise Exception("Browser driver is not inited")
        if(newTab):
            self.__performKeys__(Keys.CONTROL, 't')
            self.driver.switch_to.window(self.driver.window_handles[1])
        self.driver.get(url)
        self.wait(waitConditions, timeout=timeout)
        html = self.getPageSource()
        return self.driver.window_handles[0], html
    
    def wait(self, waitConditions:list, *, timeout:int = TIMEOUT):
        wait = WebDriverWait(self.driver, timeout)
        time.sleep(Browser.WAIT_TIMEOUT_ONCE)
        if(waitConditions):
            for condition in waitConditions:
                type = condition["type"]
                element = condition["element"]
                try:
                    if(type == Browser.ELEMENT_HIDDEN): wait.until_not(lambda element:self.isElementDisplay)
                    if(type == Browser.ELEMENT_VISIBLE): wait.until(lambda element:self.isElementDisplay)
                    if(type == Browser.ELEMENT_ENABLED): wait.until(lambda element:self.isElementEnable)
                except Exception as e:
                    Log.e(str(e))

    def getPageSource(self, tabid=None, *, node=None):
        curTab = self.driver.current_window_handle
        if(tabid and curTab != tabid):
            self.driver.switch_to.window(tabid)
        if(not node):
            node = self.findNodeBy((By.TAG_NAME, "body"))
        html = node.get_attribute('outerHTML')
        if(tabid and curTab != tabid):
            self.driver.switch_to.window(curTab)
        return html

    def __removeScriptAndStyle__(self, targetNode):
        self.driver.execute_script("""
            var node = arguments[0];
            var scripts = node.getElementsByTagName('script');
            while (scripts.length > 0) {
                scripts[0].parentNode.removeChild(scripts[0]);
            }
        """, targetNode)
        self.driver.execute_script("""
            var node = arguments[0];
            var styles = node.getElementsByTagName('style');
            while (styles.length > 0) {
                styles[0].parentNode.removeChild(styles[0]);
            }
        """, targetNode)

    def close(self, tabid):
        tabList = self.driver.window_handles
        currentTabId = tabList[0]
        for id in tabList:
            if(tabid == id):
                self.driver.switch_to.window(tabid)
                self.__performKeys__(Keys.CONTROL, 'w')
                break
        if(currentTabId != tabid): self.driver.switch_to.window(currentTabId)

    def exit(self):
        if(self.driver):
            self.driver.quit()
